
<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>ratelimiter: Go Coverage Report</title>
		<style>
			body {
				background: black;
				color: rgb(80, 80, 80);
			}
			body, pre, #legend span {
				font-family: Menlo, monospace;
				font-weight: bold;
			}
			#topbar {
				background: black;
				position: fixed;
				top: 0; left: 0; right: 0;
				height: 42px;
				border-bottom: 1px solid rgb(80, 80, 80);
			}
			#content {
				margin-top: 50px;
			}
			#nav, #legend {
				float: left;
				margin-left: 10px;
			}
			#legend {
				margin-top: 12px;
			}
			#nav {
				margin-top: 10px;
			}
			#legend span {
				margin: 0 5px;
			}
			.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

		</style>
	</head>
	<body>
		<div id="topbar">
			<div id="nav">
				<select id="files">
				
				<option value="file0">github.com/Narasimha1997/ratelimiter/attribute_limiter.go (96.2%)</option>
				
				<option value="file1">github.com/Narasimha1997/ratelimiter/limiter.go (100.0%)</option>
				
				<option value="file2">github.com/Narasimha1997/ratelimiter/window.go (100.0%)</option>
				
				</select>
			</div>
			<div id="legend">
				<span>not tracked</span>
			
				<span class="cov0">not covered</span>
				<span class="cov8">covered</span>
			
			</div>
		</div>
		<div id="content">
		
		<pre class="file" id="file0" style="display: none">package ratelimiter

import (
        "fmt"
        "sync"
        "time"
)

type AttributeMap map[string]Limiter

type AttributeBasedLimiter struct {
        attributeMap AttributeMap
        m            sync.Mutex
}

func (a *AttributeBasedLimiter) HasKey(key string) bool <span class="cov8" title="1">{
        a.m.Lock()
        _, ok := a.attributeMap[key]
        a.m.Unlock()
        return ok
}</span>

func (a *AttributeBasedLimiter) CreateNewKey(key string, limit uint64, size time.Duration) error <span class="cov8" title="1">{
        a.m.Lock()
        defer a.m.Unlock()

        if _, ok := a.attributeMap[key]; ok </span><span class="cov8" title="1">{
                return fmt.Errorf(
                        "key %s is already defined", key,
                )
        }</span>

        // create a new entry:
        <span class="cov8" title="1">a.attributeMap[key] = *NewLimiter(limit, size)
        return nil</span>
}

func (a *AttributeBasedLimiter) ShouldAllow(key string, n uint64) (bool, error) <span class="cov8" title="1">{
        a.m.Lock()
        defer a.m.Unlock()

        limiter, ok := a.attributeMap[key]
        if ok </span><span class="cov8" title="1">{
                return limiter.ShouldAllow(n)
        }</span>

        <span class="cov8" title="1">return false, fmt.Errorf("key %s not found", key)</span>
}

func (a *AttributeBasedLimiter) DeleteKey(key string) error <span class="cov8" title="1">{

        a.m.Lock()
        defer a.m.Unlock()

        if limiter, ok := a.attributeMap[key]; ok </span><span class="cov8" title="1">{
                err := limiter.Kill()
                if err != nil </span><span class="cov0" title="0">{
                        return err
                }</span>
                <span class="cov8" title="1">delete(a.attributeMap, key)
                return nil</span>
        }

        <span class="cov8" title="1">return fmt.Errorf("key %s not found", key)</span>
}

func NewAttributeBasedLimiter() *AttributeBasedLimiter <span class="cov8" title="1">{
        return &amp;AttributeBasedLimiter{
                attributeMap: make(AttributeMap),
        }
}</span>
</pre>
		
		<pre class="file" id="file1" style="display: none">package ratelimiter

import (
        "context"
        "fmt"
        "sync"
        "time"
)

type Limiter struct {
        previous      *Window
        current       *Window
        lock          sync.Mutex
        size          time.Duration
        limit         uint64
        killed        bool
        windowContext context.Context
        cancelFn      func()
}

func (l *Limiter) ShouldAllow(n uint64) (bool, error) <span class="cov8" title="1">{

        l.lock.Lock()
        defer l.lock.Unlock()

        if l.killed </span><span class="cov8" title="1">{
                return false, fmt.Errorf("function ShouldAllow called on an inactive instance")
        }</span>

        <span class="cov8" title="1">currentTime := time.Now()
        currentWindowBoundary := currentTime.Sub(l.current.getStartTime())

        w := float64(l.size-currentWindowBoundary) / float64(l.size)

        currentSlidingRequests := uint64(w*float64(l.previous.count)) + l.current.count

        if currentSlidingRequests+n &gt; l.limit </span><span class="cov8" title="1">{
                return false, nil
        }</span>

        // add current request count to window of current count
        <span class="cov8" title="1">l.current.updateCount(n)
        return true, nil</span>
}

func (l *Limiter) progressiveWindowSlider() <span class="cov8" title="1">{
        for </span><span class="cov8" title="1">{
                select </span>{
                case &lt;-l.windowContext.Done():<span class="cov8" title="1">
                        return</span>
                default:<span class="cov8" title="1">
                        toSleepDuration := l.size - time.Since(l.current.getStartTime())
                        time.Sleep(toSleepDuration)
                        l.lock.Lock()
                        // make current as previous and create a new current window
                        l.previous.setStateFrom(l.current)
                        l.current.resetToTime(time.Now())
                        l.lock.Unlock()</span>
                }
        }
}

func (l *Limiter) Kill() error <span class="cov8" title="1">{

        l.lock.Lock()
        defer l.lock.Unlock()

        if l.killed </span><span class="cov8" title="1">{
                return fmt.Errorf("called Kill on already killed limiter")
        }</span>

        <span class="cov8" title="1">defer l.cancelFn()
        l.killed = true
        return nil</span>
}

func NewLimiter(limit uint64, size time.Duration) *Limiter <span class="cov8" title="1">{
        previous := NewWindow(0, time.Now())
        current := NewWindow(0, time.Now())

        childCtx, cancelFn := context.WithCancel(context.Background())

        limiter := &amp;Limiter{
                previous:      previous,
                current:       current,
                lock:          sync.Mutex{},
                size:          size,
                limit:         limit,
                killed:        false,
                windowContext: childCtx,
                cancelFn:      cancelFn,
        }

        go limiter.progressiveWindowSlider()
        return limiter
}</span>
</pre>
		
		<pre class="file" id="file2" style="display: none">package ratelimiter

import (
        "time"
)

type Window struct {
        count     uint64
        startTime int64
}

func (w *Window) updateCount(n uint64) <span class="cov8" title="1">{
        w.count += n
}</span>

func (w *Window) getStartTime() time.Time <span class="cov8" title="1">{
        return time.Unix(0, w.startTime)
}</span>

func (w *Window) setStateFrom(other *Window) <span class="cov8" title="1">{
        w.count = other.count
        w.startTime = other.startTime
}</span>

func (w *Window) resetToTime(startTime time.Time) <span class="cov8" title="1">{
        nsTime := startTime.UnixNano()
        w.count = 0
        w.startTime = nsTime
}</span>

func NewWindow(count uint64, startTime time.Time) *Window <span class="cov8" title="1">{
        nsTime := startTime.UnixNano()
        return &amp;Window{
                count:     count,
                startTime: int64(nsTime),
        }
}</span>
</pre>
		
		</div>
	</body>
	<script>
	(function() {
		var files = document.getElementById('files');
		var visible;
		files.addEventListener('change', onChange, false);
		function select(part) {
			if (visible)
				visible.style.display = 'none';
			visible = document.getElementById(part);
			if (!visible)
				return;
			files.value = part;
			visible.style.display = 'block';
			location.hash = part;
		}
		function onChange() {
			select(files.value);
			window.scrollTo(0, 0);
		}
		if (location.hash != "") {
			select(location.hash.substr(1));
		}
		if (!visible) {
			select("file0");
		}
	})();
	</script>
</html>
